Skip to content

fix(search): consolidate dashboard, cross-mode, and composer fixes - #2736

Open
BigSimmo wants to merge 38 commits into
mainfrom
claude/answer-mode-coverage-gap-ilcwui
Open

fix(search): consolidate dashboard, cross-mode, and composer fixes#2736
BigSimmo wants to merge 38 commits into
mainfrom
claude/answer-mode-coverage-gap-ilcwui

Conversation

@BigSimmo

@BigSimmo BigSimmo commented Sep 7, 2026

Copy link
Copy Markdown
Owner

Summary

Consolidates dashboard navigation, mode-home clearing, composer sizing, submitted-query ownership, and universal cross-mode coverage. The integration preserves accessible result counts alongside the expanded answer-mode matches.

Consolidates #2736 (f2b26e3), #2727 (2a327b1), #2730 (9f1d4bf), #2725 (a2a539f), #2714 (053891b), #2716 (fef557b), #2718 (d204dce). Original heads are retained as ancestors; absorbed PRs are closed only after publication is verified.

Verification

  • npm run test -- tests/cross-mode-links.test.ts tests/universal-also-matches-mode-coverage.test.ts tests/universal-also-matches-submitted-query.test.ts tests/master-search-header.dom.test.tsx tests/audit-navigation-auth-regressions.test.ts — 80 passed in 5 files.

  • npm run format — passed; formatting committed.

  • git diff --check — passed.

  • Verification not run: full local PR/release suites; focused integration checks above were selected, and GitHub remains the merge gate. No live provider evaluation, deployment, migration or production data access was performed.

  • UI verification not run: local browser matrix; applicable hosted UI checks must pass before merge.

  • Current-base CI limitation: Make the outstanding-issues snapshot merge-safe (v2), and record the measured assessment of two other rows #2735 run 34495136858 failed only the aged ranking fixture provenance test (32 days old). clinical(governance): resolve forms permissions, test guards, and alert tiers (Bundle 1) #2763 carries the existing refresh; these bundles must not be merged past failed required checks.

  • Follow-up after first hosted CI: extracted submitted-search state and the result-usability helper; npm run check:maintainability-budgets passes at 4136/4140 dashboard lines.

  • npm run test -- tests/use-submitted-mode-search.dom.test.tsx tests/universal-also-matches-submitted-query.test.ts tests/clinical-dashboard-helpers.test.ts — 24 passed.

Risk and rollout

  • Risk: Shared dashboard and cross-mode navigation; full hosted UI and offline RAG gates remain necessary before merge.
  • Rollback: revert the consolidated merge commit as one unit; original source branches and heads remain available.
  • Provider or production effects: GitHub branch/PR updates only during consolidation. No deployment or provider write was performed.
  • RAG impact: no retrieval behaviour change — dashboard cross-mode presentation and navigation only; no document ranking, selection, generation or RPC change.
  • Production-readiness evidence: npm run check:production-readiness on the shared current-base governance integration stopped at six existing privacy release blockers (five pending and one partial). The production-readiness stage did not execute. These approvals remain outstanding.

Clinical Governance Preflight

  • Source-backed claims still require linked source verification before clinical use
  • No patient-identifiable document workflow was introduced or expanded without explicit governance approval
  • Supabase target remains Clinical KB Database (sjrfecxgysukkwxsowpy)
  • Service-role keys and private document access remain server-only
  • Demo/synthetic content remains clearly separated from real clinical sources
  • Source metadata, review status, and outdated/unknown-source behavior remain conservative
  • Deployment classification/TGA SaMD impact was checked when clinical decision-support behavior changed

Evidence for those, in the same order: this adds navigation to library records and never a clinical statement, and the governed caution above it is untouched; no patient-identifiable workflow exists in the diff; no Supabase surface is touched; src/lib/cross-mode-links.ts imports UniversalSearchGroup from @/lib/universal-search as a type only, erased at build, so the server module's snapshot, rag and supabase chain cannot enter the client bundle; the endpoint's existing demo and live split is unchanged; failure degrades to rendering nothing rather than a guess; and no clinical decision-support behaviour changed.

claude and others added 25 commits September 5, 2026 08:45
Clicking into "Find a mode" in the header mode menu drew a sharp accent
rectangle around the text while the magnifier icon sat outside it. The
input's Tailwind `outline-none` cannot win against the unlayered shared
text-field rule in globals.css, which paints an outline on any nested
input on focus-visible.

Adopt the established fix pair instead of inventing a new override: the
container becomes the `search-shell` focus owner and the input carries
`search-shell-input`, whose unlayered rule clears the outline and the
inset shadow. The Tailwind `focus-within:border-*` utility is dropped
because `.search-shell:focus-within` already owns that border shift, so
the control keeps a single focus affordance.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WJ1C29Kazq72SbLHUYZLQX
Production UI shard 1 failed on PR #2638 with a strict-mode violation:
`getByTestId("dsm-search-page")` resolved to two elements, the live one
under `mobile-composer-reserve-pad` and a hidden streaming twin beside it.
The PR's diff was two class names on the header mode-menu search box, which
that test never opens, and the same shard passed on the base commit minutes
earlier.

This is the known `#93` artefact: Next streaming can leave a hidden
duplicate page root in the tree under full-suite load. The repository
already adjudicated it as a test-side concern and ships `visibleByTestId`
for exactly this — its docstring names page-root and shell surfaces as the
intended callers, and `tests/dsm-ui-smoke.spec.ts` already scopes the same
testid that way. These two call sites in `ui-smoke.spec.ts` were simply
never converted, and the file already imports the helper.

`expectSingleSettledOwner` is deliberately not used here: the duplicate is
hidden and invisible to users, so requiring it to disappear would assert
more than the established decision supports.

Verified: `npm run test:e2e -- tests/ui-smoke.spec.ts --project=chromium
--grep "DSM"` — 2 passed (8.0s); eslint clean on the changed file.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WJ1C29Kazq72SbLHUYZLQX
Seven expansion patterns listed a long phrase alongside a shorter one that the
long phrase always contains, so the shorter alternative matched first and the
longer one could never be reached. Two are in Prescribing, one in Tools, and
four in Calculators.

The trimmed patterns are behaviour-identical: in each pair the surviving phrase
is a suffix of the deleted one and carries the same word boundaries, so every
string the old pattern matched the new one matches too. The existing unit tests
already exercise three of these rules using the long phrasing and still pass
unchanged, which is the proof that nothing moved.

No rule is added, removed, or retargeted. This deletes dead text only.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012j3M3zd5PkGfN5vJe3BiSU
Addresses the Codex review finding on PR #2714. Scoping only the
`dsm-search-page` root assertion left every locator after it resolving
across both copies, so the same strict-mode violation would reappear one
line later as soon as the #93 streaming twin exists: the query ribbon and
`dsm-search-result` in the first test, `dsm-category-filter-desktop` in the
second, and the diagnosis page root and its h1 after navigation.

Each test now owns its visible page root and derives the in-page locators
from it, as tests/dsm-ui-smoke.spec.ts already does.

Two locators deliberately stay page-scoped, because scoping them is wrong
rather than merely unnecessary: `dsm-category-filter-panel` renders through
`OverlayPortal` (result-filter-control.tsx), and the "Back to dsm-5" control
sits in the in-page nav header, which also portals out of the page root.
Both live outside the subtree, so a page-root scope cannot find them —
confirmed by the back-link assertion failing under the over-scoped form
before this correction.

Verified: `npm run test:e2e -- tests/ui-smoke.spec.ts --project=chromium
--grep "DSM"` — 2 passed (6.4s); eslint and prettier clean on the file.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WJ1C29Kazq72SbLHUYZLQX
Immutable review record for the merged sidebar reliability fix: merge commit
ee0cf48 tree is identical to branch tip a943909, fix commit 1a6adff is an
ancestor of main, no orphaned commits, remote branch already deleted.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Cm7QVRJrR4gkA7SyN61EoF
…ind it

Four small follow-ups to the "Also matches" disclosure, each independent.

The "Also in your library" tray dropped its match count from assistive tech at
every width. The visible count carries aria-hidden with a comment saying the
button's name should not read it twice, but that tray has no live region to say
it once, and below sm the count is display:none anyway. An sr-only copy now
carries it, so the closed control announces what it opens onto.

On phones the same panel showed "Tap to open", because the cross-mode lookup
waited for the click there. That is the blind door the desktop path was
deliberately built to avoid: the header could not state a count, and an empty
tray was still offered. The lookup is now eager at every width and the no-match
tray is dropped at every width. The cost is one extra cross-mode lookup per
phone search.

The page thumbnails kept the 3px accent top bar that was removed from the
best-match card, so the strip the owner asked to lose simply moved down the
page. Removed, leaving the hairline border.

One inline style={{ maxWidth }} in a design-scratch mockup became max-w-[30ch],
which puts the design-drift ratchet at 232 against its 233 ceiling. It had no
headroom, so the next PR to add a legitimate inline style would have failed on
a limit unrelated to it.

Verified: lint, typecheck, drift ratchet 232/233. Focused browser proof on the
specs the planner attributes to these files; the full Chromium suite is left to
CI, which runs it on this change.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KYV1sTeo2DHUyJBjzprTRU
…ct cleanup

MasterSearchHeader's composer effect cleanup unconditionally stripped
`data-composer-reserve` from the page-owned slot. The cleanup does not only
run on unmount: any change to the effect's dependencies re-runs it, and the
effect body then re-sets the marker about 4ms later. That leaves a window in
which the DOM lacks an attribute the page's own client render still produces,
and a page segment hydrating inside it logs a hydration mismatch on
`data-composer-reserve` (client "pending" vs server null).

Observed once on the dictionary catalogue and traced to
`commitHookEffectListUnmount` -> `setModeHomeComposerReservePending(..., false)`
with the slot not yet ready.

The cleanup now applies the same ready-gate adoption already uses: a slot is
ours to clear only once its own segment has signalled
`data-composer-slot-ready`. Nothing is stranded by skipping. The effect body
re-establishes the reserve on the next tick, and a real unmount takes the slot
element with it, so `getElementById` returns null there anyway.

Search-chrome invariant 15 is untouched: its three required clears (home media
query mismatch, suppressed composer, portal adoption fallback) all live in
`syncTarget` and the suppression branch, and the second test pins that the
suppressed-composer clear still fires on an unhydrated slot.

Evidence, same commit, instrumented Chromium on /dictionary/search:
  without the fix: +620ms REMOVE ready=false   (the window)
  with the fix:    no ready=false removal; all clears at ready=true
End state identical both ways: reserve absent, slot 80px, composer adopted.

Verification: focused unit tests 50 passed / 3 files; tsc --noEmit clean;
prettier clean on both files.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VRRn8uuZosrSaxXSB5J8S5
… now vanish

It requires the cross-mode panel to exist at 320px and to sit below the
medication results, but it never mocked /api/search/universal. That passed only
because the phone lookup was lazy, so the panel rendered a "Tap to open" header
whether or not anything was behind it. With the lookup eager the panel correctly
drops itself when the query has no cross-mode match, and the ordering assertion
went with it.

Mocks one documents-domain match for this test only, so the ordering guarantee
stays real rather than vacuous. The body is NDJSON, matching the endpoint: a
single JSON object parses to no groups and reproduces the same empty panel.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KYV1sTeo2DHUyJBjzprTRU
…ained

The aggregate went red several times on a UI-only branch while every local gate
was green, and CI log access stayed blocked by the babysit marker for the whole
session, so the failing job was never read. Recorded with the next action, which
is to unlock the marker and read the log before offering a cause.

Inbox request only. It applies to the canonical ledger via issues:reconcile
after this lands.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KYV1sTeo2DHUyJBjzprTRU
…retired mode homes came back

Clearing the search box emptied the React query but left `q` and `run=1` in the
URL. `showSharedHome` reads `run=1` straight off the URL, so it stayed suppressed
while the mode branch below it, now seeing no query, fell back to the home its
route had been consolidated away from. Two modes were affected and both were
reproduced in a browser against a running build before the fix:

- `/?mode=prescribing&q=…&run=1`, clear -> `medication-home` (the Dose/Safety/
  Monitoring tiles). `tests/ui-tools.spec.ts` asserts in a comment that this view
  is "retired and no longer reachable from any route". It was reachable, by that
  one click.
- `/documents/search?q=…&run=1`, clear -> `document-search-empty-state`, the
  "Start here" home that `documents/page.tsx` records as deliberately retired.

This is the same defect a clinician reported for Sources, in two more modes: the
URL and the composer disagreed, and the mode branch resolved the disagreement by
rendering a page that no longer exists as a destination.

The fix is one handler. `onClearQuery` now also drops the submitted search from
the URL, so the shared home renders. `appModeSelectionHref` rather than
`appModeHomeHref`: every mode reaching this handler is dashboard-owned, so its
home is the shared home at `/`, and `appModeHomeHref("prescribing")` still answers
`/medications`, which only 307s back. `/documents/search` has to leave its own
route entirely, which the selection href also does.

The pre-existing `answer` guard is kept and is load-bearing. A generated answer is
durable state the URL does not own, so the clear button must not discard one; a
browser check confirms answer mode is untouched.

Both paths are pinned by new browser cases, which is where the defect was proved
rather than inferred.

Also fixes the third governance suite that fails on a shallow clone. `#1M0J6D`
added guards to the clinical-hazard and rag-plan suites; `privacy-readiness` was
left without one and still reported `reviewedCommit does not exist: <sha>`, which
reads as a governance breach rather than a missing object. It now mirrors the two
siblings exactly. The gate is not weakened: on a full clone the commit resolves,
`checkGit` stays true, and the real ancestry check runs.

Queues a correction to ledger row `#ZKR5YK`, whose Sources, Documents and
Medication examples are gone now that all three bare paths redirect. Tools is the
only live case left, and it stays an owner ruling rather than a fix.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QaizDqAQZWHgfGLitWU8xf
…at was removed

It asserted the exact `(isWide || modeId === "answer" || expanded)` gate, which
was the deferral that produced the phone blind door. Repointed at the eager gate,
at the empty-tray guard that now runs at every width, and at the three-way header
so the "Tap to open" arm cannot come back unnoticed.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KYV1sTeo2DHUyJBjzprTRU
…queued for it

Another session had a `done` pending on the same row (4036f064), backed by an
implementation of the mode-pill change the row asks for. Two pending mutations on
one row block reconciliation for everyone, which is the race `#686WHW` records,
and a re-scope is pointless against a close that supersedes the whole row.

The findings that prompted the re-scope are verified and worth keeping, so they
are recorded in the cancel reason and in this branch's PR rather than lost: the
row's Sources, Documents and Medication examples are all gone now that those three
bare paths redirect, Favourites is settled by owner decision `#V0EDR4`, and Tools
was the only live case left at the time of the check.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QaizDqAQZWHgfGLitWU8xf
…udget

The explanatory comment pushed ClinicalDashboard.tsx to 4141 lines, one over its
4140-line no-growth budget. The gate's advice is to extract a module rather than
grow the monolith; extracting this file is a refactor of its own, so the
proportionate answer to a one-line overrun is to say the same thing in fewer
lines. The full reasoning, the measured reproduction and the mutation check all
live in the commit that carried the fix.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QaizDqAQZWHgfGLitWU8xf
…ct its alias

Tools had two homes. `/tools` is the canonical directory (filter ribbon, full
catalogue, detail panel). `/?mode=tools` rendered a separate hub-shaped launcher,
because `shouldShowSharedHome` excludes `tools` and so the dashboard had nothing
else to fall back to. Which surface a clinician saw depended only on how they
arrived: the mode pill gave them the hub, every other link gave them the
directory.

The hub's one unique feature was its verb shortcut row (Ask, Compare, Prescribe,
Safety, Docs, Refer, Forms, Saved), which answers "what am I trying to do" rather
than naming a mode. That row moves to `src/components/tools/tool-quick-actions.tsx`
and renders above the directory results, sourced from `accessibleTools` so a
category filter cannot empty it, and hidden once a query is running. With nothing
of its own left to show, `/?mode=tools` now redirects to `/tools` unconditionally,
carrying its query string through.

`localSmartExcludedToolIds` was declared three times; the shared copy in
`src/lib/tools-catalog.ts` is now the one definition.

The legacy launcher component is left in place, unreachable, rather than deleted
here: `tests/favourites-auth-gate.dom.test.tsx` still proves real guest-access
behaviour against it, and re-homing that coverage onto `/tools` is a separate
change.

Browser cases that loaded `/?mode=tools` now assert the same contracts on
`/tools`: the shortcut row, the phone filter sheet, the detail panel launch
links, and no shared search chrome on a route that owns its own filtering.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QaizDqAQZWHgfGLitWU8xf
The redirect landed clinicians on a directory with no way to type a tools
search. `/tools` deliberately renders no shared composer because it owns its
own filtering, but its filtering was reachable only through the URL: the box
lived on the `/?mode=tools` hub, and redirecting that alias took the box with
it. `tests/ui-clinical-ask.spec.ts` caught it — local-only Smart search in
tools mode has nowhere to be typed.

So the box comes across with the shortcut row, in flow rather than fixed, as a
page-owned filter and not a second shared composer. A draft typed here wins
over the URL and the shared command draft, the same precedence the hub used.
Submitting navigates to `/tools?q=…&run=1` so a result set stays shareable and
survives reload, which is what the hub's submit control already did.

Verified in a browser on the failing journey: typing a natural-language query
hides the shortcut row, submitting reaches the ranked results, Answer,
Documents and Favourites stay excluded, and neither the clinical-ask nor the
universal-search endpoint is called.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QaizDqAQZWHgfGLitWU8xf
The "Also in your library" line under an answer resolved links from four
catalogues loaded in the browser (medications, services, forms,
differentials). A clinician asking a question in Answer mode could never be
shown a matching DSM diagnosis, dictionary term, formulation, specifier,
therapy or tool, though every other mode's "Also matches" tray can reach
them through /api/search/universal.

Widen the data behind the existing line rather than replacing it. The line
keeps its disclosure, its placement inside the answer thread, its walk back
through earlier turns, and the per-record "Search in <mode>" action and
cross_mode_link_open telemetry that the mode-level tray does not have.

- buildCrossModeLinksFromUniversalSearch maps universal-search groups to the
  same CrossModeLink shape, gated on a query term naming the record at a word
  boundary, the same weight and minimum term length the differentials path
  uses. An errored or empty group contributes nothing.
- The consumed domains and their complement come from one array, so a domain
  cannot later be read by both halves and print one record twice.
- The lookup is opt-in per surface (universalMode) and off by default, so no
  surface that already mounts UniversalSearchAlsoMatches runs a second one.
- It is withdrawn while a generation is in flight, matching the rule the
  sibling tray follows on this mode, and it never fires for an answer thread
  restored onto an unsubmitted shared home.

Documents stays excluded: an answer already cites its documents in the
evidence rail and the source drawer.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015Xwq7U1Gu9Ncs2Aj3yFfMF
Ten cases for buildCrossModeLinksFromUniversalSearch, covering the parts that
would fail silently: an errored or empty group contributing nothing, a domain
the catalogue half already resolves being ignored, a substring hit rejected
because the query does not name the record at a word boundary, short query
filler dropped, a record the catalogue half already listed not repeated, the
per-mode and total caps, and a distinct React key when a domain omits its id.

One case pins the two domain lists as exact complements, because that is the
property which stops a future domain being read by both halves and printing
one record twice.

The coverage register's Answer exemption now states the current contract
rather than only the 2026-08-26 duplication history, and a new case pins the
universalMode opt-in on the answer surface. Without it the exemption's claim
that Answer answers cross-mode discovery elsewhere quietly stops being true.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015Xwq7U1Gu9Ncs2Aj3yFfMF
Review of the first commit found an unstated consequence: the catalogue half
caps itself at four links and the universal half added up to two more, so the
line's ceiling moved from four rows to six without anyone deciding it. Fill to
a combined five instead. The widened reach keeps its full two rows on the
common answer, which names no medication, service, form or differential at
all, and a thread that already resolved four clinical records grows by one row
rather than three.

The new browser test is the proof the unit tests cannot give: an answer for a
question naming a DSM diagnosis, with only /api/search/universal mocked, must
show a Bipolar I Disorder card inside the answer's own library line, with both
its open and its search-in-mode controls. It also asserts the request carries
exactly the six domains the line consumes, which is what stops a record
arriving down both paths, and re-asserts that this surface still has one panel
and not two.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015Xwq7U1Gu9Ncs2Aj3yFfMF
Section 12.8 records the 2026-08-26 decision to keep one cross-mode panel
under an answer and says which one won. It does not say that the winner
reached four catalogues while the panel it replaced reached twelve domains,
so a reader working from that section would conclude the surfaces were
equivalent. They were not, and the gap is invisible from the screenshots the
section was written around.

Add 12.9: the coverage that was lost, why the fix widens the line rather than
undoing the decision, the three constraints that keep it inside 12.8, the new
five-link ceiling, and the five modes that remain unreachable because they
contribute no cross-entity search domain at all.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015Xwq7U1Gu9Ncs2Aj3yFfMF
@coderabbitai

coderabbitai Bot commented Sep 7, 2026

Copy link
Copy Markdown
Contributor

Important

  • 🔍 Trigger review

This repository does not receive automatic reviews because it has fewer than 10 stars.

⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Advanced

Run ID: 95233347-e8f4-405c-99de-413146fb2924


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@supabase

supabase Bot commented Sep 7, 2026

Copy link
Copy Markdown

This pull request has been ignored for the connected project sjrfecxgysukkwxsowpy because there are no changes detected in supabase directory. You can change this behaviour in Project Integrations Settings ↗︎.


Preview Branches by Supabase.
Learn more about Supabase Branching ↗︎.

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Sep 7, 2026

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review Completed 2026-09-07T12:39:44.992099Z e3076dd Manual request
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@cursor

cursor Bot commented Sep 7, 2026

Copy link
Copy Markdown
Contributor

Bugbot couldn't run - usage limit reached

Bugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit.

A user or team admin can review and increase usage limits in the Cursor dashboard.

(requestId: serverGenReqId_e7dd1b46-4d96-45ce-8eb6-7cac3580988c)

Owner decision, 2026-09-07, reversing the collapsed one-line disclosure that
direction B introduced on 2026-08-26. The answer surface now renders the same
open card strip every other consumer of this block renders, so the same
component no longer behaves one way under an answer and another way
everywhere else, and the matches are readable without spending a tap.

What does not change: the block stays below the governed caution and above the
follow-up suggestions, keeps its two per-card actions and their telemetry, and
keeps the widened data source added earlier on this branch.

CrossModeLinksLine is left in place rather than deleted. It has no consumer
now, and it is one prop away from being the presentation again if the owner
wants it back.

Three browser tests carried the disclosure as their contract. Each is updated
rather than dropped, and each now asserts the absence of a trigger alongside a
visible rail, so a disclosure reappearing under an answer fails the suite. The
1280px case keeps its computed-display assertion on the rail, because width was
where the previous design broke and that class of failure is still worth
catching.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015Xwq7U1Gu9Ncs2Aj3yFfMF
Section 12.9 explained the widened data source while still describing the
collapsed line as the presentation. Record that the line is gone, what
reversing it costs and buys, why CrossModeLinksLine stays in the file with no
consumer, and how the three browser tests that carried the disclosure were
turned into guards against it returning.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015Xwq7U1Gu9Ncs2Aj3yFfMF

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: e3076dd326

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/lib/cross-mode-links.ts Outdated
@BigSimmo

BigSimmo commented Sep 7, 2026

Copy link
Copy Markdown
Owner Author

@codex resolve actionable Codex review findings for this pull request and current head using the repository instructions. This is the pull request's single automatic repair pass: do not perform a fresh review, create new standalone findings, or request another review. Work only the existing unresolved Codex threads on the current head. The only repository destination is BigSimmo/Database, and the only branch destination is the pull request head branch claude/answer-mode-coverage-gap-ilcwui at starting commit e3076dd; never publish fixes to a detached or synthetic work branch and never create a stacked pull request. Use the authenticated GitHub connector to commit each approved fix to BigSimmo/Database:claude/answer-mode-coverage-gap-ilcwui, then verify that the pull request head contains the published commit before reporting success. Always fix P0 and P1 findings. For P2 and lower findings, fix only clear, scoped, low-risk issues; otherwise disposition them with a concise reason. For a fixed thread, reply with as the first line and as the second line. For a no-code disposition, use followed by . These result markers authorize the workflow to close that exact thread only after it verifies a fixed commit is the pull request head; a local-only commit is not a fix. If publication or verification fails, do not use either result marker, do not claim success, and leave the thread open with the blocker. If human input or new authorization is required, do the same. Finish only after every actionable thread is fixed or dispositioned and closed, or explicitly left open for a human decision. Do not update the branch from main, address unrelated reviews, broaden scope, or create more than one scoped fix commit. Do not use external APIs, paid services, credentials, dependency changes, or broad refactors unless explicitly authorized. Add targeted tests where behavior changes and run the narrowest relevant validation.

Codex review finding on PR #2736, and it is real. The mapper replaced the
API's within-domain ordering with a term-count score, and that score ties
constantly: most matches hit exactly one query term. The comparator then fell
through to alphabetical title order, so among equally scoring items from one
domain the earliest title won. With maxPerMode at 1 that is not a cosmetic
reordering, it silently drops the domain's actual top result and shows its
second.

Carry the item's position in the group the server returned it in and use it as
the tiebreaker ahead of the title. Equal score and equal mode priority means
the same domain, so that comparison is always within a domain and never an
incomparable cross-domain one. item.score is deliberately still unused: its
own type says it is comparable within a group only.

The new case is the one the reviewer described: a higher-ranked "Zzz" item
ahead of a lower-ranked "Aaa" item in the same domain, both matching the same
terms. Alphabetical order would take the Aaa record; the domain's ranking
takes the Zzz one.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015Xwq7U1Gu9Ncs2Aj3yFfMF
@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Nice work!

Reviewed commit: e3076dd326

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Five was set while this block was still a collapsed disclosure, where an extra
row cost nothing until the reader opened it. It is open at every width now, so
every row is unconditional height on the answer surface, between the governed
caution and the follow-ups. Four is the ceiling the catalogue half alone
allowed before the widened lookup existed, so holding it keeps this change
about reach rather than about size.

The cap only binds when the catalogue half filled the strip on its own, and a
question naming four medication, service, form or differential records is
already well served. The common answer names none of them, which is where the
widened reach earns its place and where this number never applies.

Renamed the constant: there is no line any more.

Also records in the handover why CrossModeLinksLine is still in the file. The
owner asked for it to be deleted; check:dead-code-candidate refused, because the
symbol was introduced on 2026-08-26 and sits inside the gate's 30-day window.
The heuristic is wrong here — its consumer existed and was removed the same day
— but tuning a threshold to pass a diff is what that gate exists to stop, so it
stays until the window lapses.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015Xwq7U1Gu9Ncs2Aj3yFfMF
Owner decision, 2026-09-07, after seeing both ends of this built. Neither was
right.

Closed by default, as direction B shipped it, hides the matches behind a tap
nobody has a reason to take: a closed tray cannot show what it holds, so the
reader spends the tap to find out whether it was worth spending. Removing the
toggle entirely, the first correction, put four unconditional rows into the
answer's evidence stack with no way to put them away, on the densest surface in
the product.

The hybrid is one control that starts open. The matches are readable without a
tap and the space is one tap away. variant="line" is the answer surface's
presentation again, so the card strip stays what the DSM diagnosis page and the
mockups use.

The three browser tests that carry this contract now pin the resting state as
open and exercise the collapse from there. The 1280px case asserts the rail's
own computed display in both states rather than the trigger's word for it: a
`hidden` beside a `md:flex` in one class list loses to the media query from
768px up, which once left a rail painted open while its trigger reported it
closed. Resting open, the same mechanic would hide a broken collapse instead,
so the closed state is the one pinned on computed display.

The ceiling stays at four. Its height argument is weaker now the line can be
put away, but four is the number the catalogue half alone allowed and there is
no reason to spend the difference without asking.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015Xwq7U1Gu9Ncs2Aj3yFfMF
Owner decision, 2026-09-07. The header listed the first three record names
beside the label from sm up, so the reader saw "Bipolar disorder · East
Metropolitan Eating Disorder Specialist Service · Kara Maar Spe…" and then the
same records as cards immediately underneath.

Those names were doing real work while the line rested closed: a closed tray
cannot show what it holds, so the preview was the only way to tell the reader
whether opening it was worth a tap. The line rests open now, so the preview
repeats what is already on screen and truncates mid-word to do it.

The header is label, rule and count at every width, which is the same editorial
device the sibling "Also matches" tray uses. The rule no longer stops at sm,
because the names are no longer taking its place from there up.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015Xwq7U1Gu9Ncs2Aj3yFfMF
@cursor

cursor Bot commented Sep 7, 2026

Copy link
Copy Markdown
Contributor

Bugbot couldn't run - usage limit reached

Bugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit.

A user or team admin can review and increase usage limits in the Cursor dashboard.

(requestId: serverGenReqId_cb631f76-8097-4ccf-bbe3-6c10d873f6fe)

@BigSimmo
BigSimmo enabled auto-merge September 7, 2026 15:31
@cursor

cursor Bot commented Sep 7, 2026

Copy link
Copy Markdown
Contributor

Bugbot couldn't run - usage limit reached

Bugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit.

A user or team admin can review and increase usage limits in the Cursor dashboard.

(requestId: serverGenReqId_b699fd59-5081-423b-bd35-480616e71325)

@cursor

cursor Bot commented Sep 8, 2026

Copy link
Copy Markdown
Contributor

Bugbot couldn't run - usage limit reached

Bugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit.

A user or team admin can review and increase usage limits in the Cursor dashboard.

(requestId: serverGenReqId_9ff49530-be56-4267-98a6-00790d5594a6)

claude and others added 4 commits September 8, 2026 12:51
…query

Editing the bottom composer on a result view calls setQuery alone: the
results and modeSearchSubmitted both stay as they were. The lookup was
keyed on that live query, so a paused draft fetched cross-mode matches
for a search the reader never ran, replaced the tray and its "N related
modes" count, and left those matches disagreeing with the primary cards
still on screen for the last submitted search.

The dashboard now holds the submitted query separately from the draft.
Every submission records the text it submitted, clearing the flag clears
the query with it, and an auto-run URL seeds it so a restored result view
is not blank. Answer mode is unchanged, since it already keyed off the
generated answer's query. Tools and Favourites never record a submission
and fall through to the draft, which is the only query they have.

Prescribing is deliberately left alone. Its medication list is itself
debounced against the same draft, so the tray following the draft agrees
with the cards above it there rather than contradicting them.

tests/universal-also-matches-submitted-query.test.ts pins the derivation,
that only the submission wrapper may write the submitted query, and that
every submission passes its text. 7 passed, and 113 passed across the 13
dashboard, seed, thread and also-matches suites.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0142trjgjRAP2GA9wzqoYUAE
@BigSimmo BigSimmo changed the title Answer library line reaches the modes no local catalogue can resolve fix(search): consolidate dashboard, cross-mode, and composer fixes Sep 10, 2026
@cursor

cursor Bot commented Sep 10, 2026

Copy link
Copy Markdown
Contributor

Bugbot couldn't run - usage limit reached

Bugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit.

A user or team admin can review and increase usage limits in the Cursor dashboard.

(requestId: serverGenReqId_0315f98f-ce48-4609-9b8d-23c9654bc5e8)

@github-actions

github-actions Bot commented Sep 10, 2026

Copy link
Copy Markdown
Contributor

CI triage

CI failed on this PR. Automated classification of the 2 failed job(s):

  • Unit coverageneeds investigation: inspect the failing step and uploaded diagnostics; rerun only after classifying the cause.
  • PR requiredneeds investigation: inspect the failing step and uploaded diagnostics; rerun only after classifying the cause.

Compared with main CI run #16436 (failure).

Classification is evidence routing, not permission to ignore a failure. Exact quarantined Playwright identities remain governed by the flake ledger.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants